use super::vm::{
    AstBlock, AstBlockType, AstToken, AstTokenType, FunType, OperatorType, ReferencingValue,
    StackValue, SvnToken, VariableScopeType, VariableType, VariableValue,
};
use serde_json;
pub struct TokenDecoder {}

impl TokenDecoder {
    pub fn parse(code: String) -> Result<Vec<SvnToken>, String> {
        let res = serde_json::from_str::<serde_json::Value>(&code);
        return if let Ok(v) = res {
            let temp = v.as_array();
            if let Some(ar) = temp {
                TokenDecoder::decode_vec(ar.to_vec())
            } else {
                Err("script data parse error".to_string())
            }
        } else {
            Err("script data error".to_string())
        };
    }
    pub fn parse_json_value(val:serde_json::Value) -> Result<Vec<SvnToken>, String>{
        let temp = val.as_array();
        if let Some(ar) = temp {
            TokenDecoder::decode_vec(ar.to_vec())
        } else {
            Err("script data parse error".to_string())
        }
    }
    fn get_operator(operator: &str) -> OperatorType {
        let mut opt = OperatorType::Add;
        if operator.eq("+") {
            opt = OperatorType::Add;
        } else if operator.eq("-") {
            opt = OperatorType::Subtract;
        } else if operator.eq("*") {
            opt = OperatorType::Multiply;
        } else if operator.eq("/") {
            opt = OperatorType::Divide;
        } else if operator.eq("%") {
            opt = OperatorType::Modulo;
        } else if operator.eq("==") {
            opt = OperatorType::Equal;
        } else if operator.eq("!=") {
            opt = OperatorType::NotEqual;
        } else if operator.eq("<") {
            opt = OperatorType::LessThan;
        } else if operator.eq(">") {
            opt = OperatorType::GreaterThan;
        } else if operator.eq(">=") {
            opt = OperatorType::GreaterThanOrEqual;
        } else if operator.eq("<=") {
            opt = OperatorType::LessThanOrEqual;
        } else if operator.eq("==") {
            opt = OperatorType::Equal;
        } else if operator.eq("!=") {
            opt = OperatorType::NotEqual;
        } else if operator.eq("||") {
            opt = OperatorType::Or;
        } else if operator.eq("&&") {
            opt = OperatorType::And;
        }
        opt
    }
    fn decode_vec(ar: Vec<serde_json::Value>) -> Result<Vec<SvnToken>, String> {
        let mut res: Vec<SvnToken> = Vec::new();
        for v in ar {
            let t_value = &v["value"];
            let t_operator = &v["operator"];
            let t_body = &v["body"];
            let mut scope = "";
            if let Some(s)=&v["scope"].as_str(){
                scope = *s;
            }
            if !t_value.is_null() {
                // stack variable
                let tp_node_id = &v["nodeId"].as_str();
                let tp_id = &v["id"].as_str();
                let mut id = String::new();
                if let Some(v) = tp_id {
                    id = v.to_string();
                }
                let node_type = &v["type"].as_str();
                let mut n_type = VariableType::Number;
                let mut tp_val = VariableValue::Number(0.0);
                let tp_scope_type = &v["scopeType"].as_str();
                if let Some(t) = node_type {
                    if t.to_owned() == "number" {
                        n_type = VariableType::Number;
                        if let Some(val) = v["value"].as_str() {
                            if let Ok(real_val) = val.parse::<f64>(){
                                tp_val = VariableValue::Number(real_val);
                            }else{
                                return Err((id + " value not number").to_string());
                            }

                        }else if let Some(val) = v["value"].as_number() {
                            if let Some(real_val) = val.as_f64(){
                                tp_val = VariableValue::Number(real_val);
                            }else{
                                return Err((id + " value not number").to_string());
                            }

                        }
                    } else if t.to_owned() == "string" {
                        n_type = VariableType::String;
                        let opt_val = v["value"].as_str();
                        if let Some(val) = opt_val {
                            tp_val = VariableValue::String(val.to_owned());
                        }
                    } else if t.to_owned() == "boolean" {
                        n_type = VariableType::Boolean;
                        let opt_val = v["value"].as_bool();
                        if let Some(val) = opt_val {
                            tp_val = VariableValue::Boolean(val);
                        }
                    } else if t.to_owned() == "array" {
                        n_type = VariableType::Array;
                        let opt_val = v["value"].as_array();
                        if let Some(val) = opt_val {
                            let mut arr: Vec<String> = Vec::new();
                            for v in val {
                                arr.push(v.to_string());
                            }
                            tp_val = VariableValue::Array(arr);
                        }
                    } else if t.to_owned() == "referencing" {
                        n_type = VariableType::Referencing;
                        let opt_val = v["value"].as_str();
                        if let Some(val) = opt_val {
                            tp_val = VariableValue::String(val.to_owned());
                        }
                    }
                }
                
                let mut scope_type = VariableScopeType::Global;
                if let Some(s) = tp_scope_type {
                    if s.contains("global") {
                        scope_type = VariableScopeType::Global;
                    } else if s.contains("builtin") {
                        scope_type = VariableScopeType::Builtin;
                    } else {
                        scope_type = VariableScopeType::Temp;
                    }
                }
                let node_id = match tp_node_id {
                    None => id.clone(),
                    Some(v) => v.to_string()
                };
                let svalue = StackValue::new(node_id, id.clone(), n_type, tp_val, scope.to_string(), scope_type);
                let mut token = SvnToken::new(id.clone(),svalue.id.clone(),scope.to_string());
                token.value = Some(svalue);
                res.push(token);
            } else if !t_operator.is_null() {
                // stack operator
                let tp_node_id = &v["nodeId"].as_str();
                let tp_id = &v["id"].as_str();
                let mut id = String::new();
                if let Some(v) = tp_id {
                    id = v.to_string();
                }
                let tp_opt = &v["operator"].as_str();
                let tp_operator: &str;
                if let Some(s) = tp_opt {
                    tp_operator = s;
                } else {
                    return Err((id + " operator not found").to_string());
                }
                let node_id = match tp_node_id {
                    None => id.clone(),
                    Some(v) => v.to_string()
                };
                let token_type = AstTokenType::BinaryExpression;
                let operator = TokenDecoder::get_operator(tp_operator);
                let tp_left = &v["left"];
                if tp_left.is_null() {
                    return Err(id + " left value not found");
                }
                let tp_left_name = tp_left["name"].as_str();
                let tp_left_val;
                if let Some(s) = tp_left_name {
                    tp_left_val = s.to_owned();
                } else {
                    return Err(id + " left value error");
                }
                let left = ReferencingValue { name: tp_left_val };

                let tp_right = &v["right"];
                if tp_right.is_null() {
                    return Err(id + " right value not found");
                }
                let tp_right_name = tp_right["name"].as_str();
                let tp_right_val;
                if let Some(s) = tp_right_name {
                    tp_right_val = s.to_owned();
                } else {
                    return Err(id + " right value error");
                }
                let right = ReferencingValue { name: tp_right_val };

                let ast = AstToken::new(node_id, id.clone(), token_type, operator, left, right);
                let mut token = SvnToken::new(id.clone(),ast.id.clone(),scope.to_string());
                token.ast = Some(ast);
                res.push(token);
            } else if !t_body.is_null() {
                // stack block
                let tp_node_id = &v["nodeId"].as_str();
                let tp_id = &v["id"].as_str();
                let mut id = String::new();
                if let Some(v) = tp_id {
                    id = v.to_string();
                }
                let node_id = match tp_node_id {
                    None => id.clone(),
                    Some(v) => v.to_string()
                };
                let mut block_type = AstBlockType::CallFunction;
                let block_type_tp = v["type"].as_str();
                if let Some(s) = block_type_tp {
                    if s.eq("CallFunction") {
                        block_type = AstBlockType::CallFunction;
                    } else if s.eq("Function") {
                        block_type = AstBlockType::Function;
                    } else if s.eq("Logic") {
                        block_type = AstBlockType::Logic;
                    } else if s.eq("Loop") {
                        block_type = AstBlockType::Loop;
                    } else if s.eq("CountLoop") {
                        block_type = AstBlockType::CountLoop;
                    }  else if s.eq("SetVariable") {
                        block_type = AstBlockType::SetVariable;
                    } else if s.eq("ReturnFunction") {
                        block_type = AstBlockType::ReturnFunction;
                    }else if s.eq("ExecuteFunction") {
                        block_type = AstBlockType::ExecuteFunction;
                    }
                }

                let func_name_tp = v["funcName"].as_str();
                let func_name;
                if let Some(s) = func_name_tp {
                    func_name = Some(s.to_owned());
                } else {
                    func_name = None;
                }
                let fun_type_tp = v["funType"].as_str();
                let mut fun_type = FunType::Sync;
                if let Some(s) = fun_type_tp {
                    if s.eq("async") {
                        fun_type = FunType::Async;
                    } else {
                        fun_type = FunType::Sync;
                    }
                }
                let mut block_body = vec![];
                let body_tp = v["body"].as_array();
                if let Some(children) = body_tp {
                    let body = TokenDecoder::decode_vec(children.to_owned());
                    match body {
                        Ok(b) => {
                            block_body = b.clone();
                        }
                        Err(e) => return Err(e),
                    }
                }
                let mut args: Option<Vec<ReferencingValue>> = None;
                if let Some(args_temp) = v["args"].as_array() {
                    let mut args_val = Vec::new();

                    for val in args_temp {
                        if let Some(arg_name) = val["name"].as_str() {
                            let arg = ReferencingValue {
                                name: arg_name.to_owned(),
                            };
                            args_val.push(arg);
                        }
                    }
                    args = Some(args_val);
                }
                let block = AstBlock::new(
                    node_id, id.clone(), block_type, block_body, func_name, args, fun_type,
                );
                let mut token = SvnToken::new(id.clone(),block.id.clone(),scope.to_string());
                token.block = Some(block);
                res.push(token);
            }
        }

        Ok(res)
    }
}
